﻿using System;
using System.Threading;
using System.IO;
using CloundUploader.Properties;

namespace CloundUploader.Framework.Upload
{
    public class Uploader
    {
        private BaseFileUploader FileUploader;
        public User.User User;

        public UploadFileInfo UploadFile;
        public long Size;
        public double Speed;
        public double Process;

        private Thread _mainThread;
        private Exception _lastError;
       

        public event EventHandler StateChanged;
        public event EventHandler Ending;
        public event EventHandler InfoReceived;



        public Uploader(UploadFileInfo file)
        {
            this.UploadFile = file;
            if (UploadFile.IsLocal())
            {
                FileUploader.CreatData();
            }


        }
        private void SetState(UploaderState value)
        {
            State = value;
            OnStateChanged();
        }

        protected virtual void OnStateChanged()
        {
            if (StateChanged != null)
            {
                StateChanged(this, EventArgs.Empty);
            }
        }
      

        public long FileSize
        {
            get
            {
                return Size;
            }
        }
      public Exception LastError
        {
            get { return _lastError; }
            set { _lastError = value; }
        }

        public UploaderState State { get; set; }
        public bool IsWorking()
        {
            UploaderState state = this.State;
            return (state == UploaderState.Preparing ||
                state == UploaderState.WaitingForReconnect ||
                state == UploaderState.Working);
        }
        private void StartToPrepare()
        {
            _mainThread = new Thread(StartUploadThreadProc);
            _mainThread.IsBackground = true;
            _mainThread.Start();
        }

        //private void StartPrepared()
        //{
        //    mainThread = new Thread(new ThreadStart(RestartDownload));
        //    mainThread.Start();
        //}
        protected virtual void OnEnding()
        {
            if (Ending != null)
            {
                Ending(this, EventArgs.Empty);
            }
        }
        protected virtual void OnInfoReceived()
        {
            if (InfoReceived != null)
            {
                InfoReceived(this, EventArgs.Empty);
            }
        }
        public void WaitForConclusion()
        {
            if (!IsWorking())
            {
                if (_mainThread != null && _mainThread.IsAlive)
                {
                    _mainThread.Join(TimeSpan.FromSeconds(1));
                }
            }

            while (IsWorking())
            {
                Thread.Sleep(100);
            }

            // Debug.WriteLine(this.State.ToString());
        }
        public void Pause()
        {
            if (State == UploaderState.Preparing || State == UploaderState.WaitingForReconnect)
            {


                _mainThread.Abort();
                _mainThread = null;
                SetState(UploaderState.NeedToPrepare);
                return;
            }

            if (State == UploaderState.Working)
            {
                SetState(UploaderState.Pausing);

                _mainThread.Abort();
                _mainThread = null;

                SetState(UploaderState.Paused);
            }
        }
        public void Start()
        {
            //FileUploader.setCookie(userinfor.getWebUserByName("豆丁").Cookie);
            if (State == UploaderState.NeedToPrepare)
            {
                SetState(UploaderState.Preparing);

                StartToPrepare();
            }
            else
            //State != UploaderState.Preparing &&
            //State != UploaderState.Pausing &&
            //State != UploaderState.Working &&
            //State != UploaderState.WaitingForReconnect)
            {
                SetState(UploaderState.Preparing);

                StartPrepared();
            }
        }
        private void StartPrepared()
        {
            _mainThread = new Thread(new ThreadStart(StartUploadThreadProc));
            _mainThread.IsBackground = true;
            _mainThread.Start();
        }

        private void StartUploadThreadProc()
        {
            SetState(UploaderState.Preparing);
            Stream inputStream = null;
            Stream outStream = null;
            int currentTry = 0;
            do
            {
                _lastError = null;

                if (State == UploaderState.Pausing)
                {
                    SetState(UploaderState.NeedToPrepare);
                    return;
                }

                SetState(UploaderState.Preparing);

                currentTry++;
                try
                {

                   
                        inputStream = new FileStream(UploadFile.Src, FileMode.Open, FileAccess.Read, FileShare.Read);
                        // inputStream = new FileStream(UploadFile.src, FileMode.Open, FileAccess.Read);
                        Size = inputStream.Length;

                   

                    if (Size != 0)
                    {
                        int sizeM = (int)(Size / 1024 / 1024);

                        if (sizeM < UploaderContext.Instance.User.UploadSetting.FileMin || sizeM > UploaderContext.Instance.User.UploadSetting.FileMax)
                        {
                            SetState(UploaderState.Ended);
                            inputStream.Close();
                            return;
                        }
                    }

                    outStream = FileUploader.CreatStream(Size);

                    break;
                }
                catch (ThreadAbortException)
                {
                    SetState(UploaderState.NeedToPrepare);
                    if (inputStream != null)
                    {
                        inputStream.Close();
                    }
                    if (outStream != null)
                    {
                        outStream.Close();
                    }
                    return;
                }


                catch (Exception ex)
                {
                    if (inputStream != null)
                    {
                        inputStream.Close();
                    }
                    if (outStream != null)
                    {
                        outStream.Close();
                    }
                    _lastError = ex;
                    if (currentTry < UploaderContext.Instance.User.UploadSetting.MaxRetries)
                    {
                        SetState(UploaderState.WaitingForReconnect);
                        Thread.Sleep(TimeSpan.FromSeconds(UploaderContext.Instance.User.UploadSetting.RetryDelay));
                    }
                    else
                    {
                        SetState(UploaderState.EndedWithError);
                        return;
                    }
                }
            }
            while (true);

            try
            {
                _lastError = null;
                StartUpload(ref inputStream, ref outStream);
            }
            catch (ThreadAbortException)
            {
                if (inputStream != null)
                {
                    inputStream.Close();
                }
                if (outStream != null)
                {
                    outStream.Close();
                }
                throw;
            }
            catch (Exception ex)
            {
                _lastError = ex;
                if (inputStream != null)
                {
                    inputStream.Close();
                }
                if (outStream != null)
                {
                    outStream.Close();
                }
                SetState(UploaderState.EndedWithError);
            }
        }
        private void StartUpload(ref Stream inputStream, ref Stream outStream)
        {
            // notifies
            OnInfoReceived();

            byte[] buffer = new byte[8192];
            SetState(UploaderState.Working);
            FileUploader.PostHeadData(outStream);
            int bytesRead = 0;
            double bytesSent = 0;
            double totalBytes = Size;
            // inputStream.Position = 0;
            System.Diagnostics.Stopwatch timer = new System.Diagnostics.Stopwatch();//计时器
            timer.Start();
            while ((bytesRead = inputStream.Read(buffer, 0, buffer.Length)) != 0)
            {
                if (State == UploaderState.Working)
                {


                    outStream.Write(buffer, 0, bytesRead);
                    bytesSent += bytesRead;
                    Process = (double)bytesSent / (double)totalBytes * 100.0f;
                    double t = timer.ElapsedMilliseconds / 1000;
                    t = t <= 0 ? 1 : t;
                    Speed = (double)bytesSent / t;
                    outStream.Flush();

                    //   inputStream.Flush();


                }
                else
                {
                    SetState(UploaderState.NeedToPrepare);
                }
            }

            FileUploader.PostEndData(outStream);
            buffer = null;
            if (inputStream != null)
            {
                inputStream.Close();
            }
            if (outStream != null)
            {
                outStream.Close();
            }
            if (FileUploader.GetResponse())
            {
                FileUploader.PostData();
            };

            SetState(UploaderState.Ended);
            if (this.State != UploaderState.Pausing)
            {
                OnEnding();
            }

            SetState(UploaderState.Ended);


        }

    }
}
